Continuing after Project-1 where we did Flower Classification using the MobilenetV2, we now explore VGG16 and Xception models for the same dataset and compare the performance of these models.
A network model that has been saved and earlier trained using a sizable dataset is known as a pre-trained model.
The concept behind transfer learning for image classification is that if we use a model that was trained on a really big, representative dataset, this model can serve as our basic model for categorising images. This allows to save a lot of training time by utilizing the feature maps.
VGG16 - The VGG16 architecture consists of 16 layers, including 13 convolutional layers and 3 fully connected layers. The convolutional layers have small 3x3 filters and are followed by max-pooling layers. The number of filters in the convolutional layers increases as we move deeper into the network. The fully connected layers have 4096 neurons each and are followed by a softmax layer for classification. The model has more than 138 million parameters and is pre-trained on the ImageNet dataset, achieving top results in image classification and object detection tasks.
Xception - The Xception model consists of a total of 36 convolutional layers, which are organized into 14 modules. The first module is a standard convolutional layer followed by a max-pooling layer. The subsequent 13 modules follow a similar pattern of depthwise separable convolutional layers, where the depthwise convolution is followed by a pointwise convolution. The number of filters in the depthwise convolution increases in the middle layers and decreases in the final layers. The output of each module is then added to the previous output using a skip connection. The final layers include global average pooling, a fully connected layer, and a softmax layer for classification.
# import stuff
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import seaborn as sns
from PIL import Image
from IPython.display import Image, display
import os
from keras.applications import xception
from sklearn.metrics import confusion_matrix
from sklearn.datasets import load_files
from sklearn.model_selection import train_test_split
from keras.models import Model
from keras import Model, optimizers
import tensorflow_hub as hub
from keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.optimizers import Adam, RMSprop, SGD
from tensorflow.keras.applications import ResNet50, VGG16, VGG19, MobileNetV2
from tensorflow.keras.applications.resnet50 import preprocess_input as prepro_res50
from tensorflow.keras.applications.vgg19 import preprocess_input as prepro_vgg19
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, Dense, Flatten, GlobalAveragePooling2D, BatchNormalization, Dropout, MaxPool2D, MaxPooling2D
# load the backend
from keras import backend as K
# prevent Tensorflow memory leakage
K.clear_session()
path_data = r'D:\T431 - George Brown\DL Math 2\Flower_Classication_using_Transfer_Learning\flowers'
print(os.listdir(path_data))
['daisy', 'dandelion', 'rose', 'sunflower', 'tulip']
from os.path import join
img_folders = [join(path_data, folder) for folder in os.listdir(path_data)]
list(img_folders)
['D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\daisy', 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\dandelion', 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\rose', 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\sunflower', 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\tulip']
data_dir = r'D:\T431 - George Brown\DL Math 2\Flower_Classication_using_Transfer_Learning\flowers'
data = load_files(data_dir, random_state=28, shuffle=True)
X = np.array(data['filenames']) # files location of each flower
y = np.array(data['target']) # target label of each flower
labels = np.array(data['target_names'])
# remove eventual .pyc or .py files
pyc_file = (np.where(file==X) for file in X if file.endswith(('.pyc','.py')))
for pos in pyc_file:
X = np.delete(X, pos)
y = np.delete(y, pos)
print(f'Data files - {X}')
print(f'Target labels - {y}') # numbers are corresponding to class label,
# we have to change them to a vector of 5 elements
print(f'Name labels - {labels}')
print(f'Number of training files : {X.shape[0]}')
Data files - ['D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\rose\\2265390547_2409007cef_n.jpg' 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\dandelion\\34689593326_0fd3fbc38a_n.jpg' 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\tulip\\10094729603_eeca3f2cb6.jpg' ... 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\dandelion\\5598845098_13e8e9460f.jpg' 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\tulip\\5757091018_cdfd79dfa6_m.jpg' 'D:\\T431 - George Brown\\DL Math 2\\Flower_Classication_using_Transfer_Learning\\flowers\\dandelion\\34537877932_f9a3476a4d_n.jpg'] Target labels - [2 1 4 ... 1 4 1] Name labels - ['daisy' 'dandelion' 'rose' 'sunflower' 'tulip'] Number of training files : 4317
# Flower species number
df = pd.DataFrame({'species': y})
print(df.shape)
df.head()
(4317, 1)
| species | |
|---|---|
| 0 | 2 |
| 1 | 1 |
| 2 | 4 |
| 3 | 2 |
| 4 | 4 |
# associate names to species number
df['flower'] = df['species'].astype('category')
df['flower'].cat.categories = labels
df.head()
C:\Users\pinku\AppData\Local\Temp\ipykernel_33048\1180994331.py:3: FutureWarning: Setting categories in-place is deprecated and will raise in a future version. Use rename_categories instead. df['flower'].cat.categories = labels
| species | flower | |
|---|---|---|
| 0 | 2 | rose |
| 1 | 1 | dandelion |
| 2 | 4 | tulip |
| 3 | 2 | rose |
| 4 | 4 | tulip |
fig, ax = plt.subplots()
ax = sns.countplot(x="flower", data=df)
ax.set(ylabel='Count', title='Flower species distribution')
ax.tick_params(axis='x', rotation=15)
Now, we load the different images and transform them into numpy arrays
image_size = 224 # standard value for Transfer learning usecase (MobileNet, ResNet50, VGG16, VGG19)
def read_and_prep_images(img_paths, img_height=image_size, img_width=image_size):
imgs = [load_img(img_path, target_size=(img_height, img_width)) for img_path in img_paths] # load image
img_array = np.array([img_to_array(img) for img in imgs]) # image to array
return(img_array)
X = np.array(read_and_prep_images(X))
print(X.shape) # (4323, 224, 224, 3) = (num_images, height_size, width_size, depth=RGB)
(4317, 224, 224, 3)
Plotting some random flowers
N = 18 # flowers to display
fig, axes = plt.subplots(3, 6, figsize=(16,6))
for ax, j in zip(axes.flat, np.random.randint(0, len(X), N)):
ax.imshow(X[j].astype(np.uint8))
ax.set(title=f'Flower: {labels[y[j]]}', xticks=[], yticks=[])
fig.tight_layout()
num_classes = len(np.unique(y))
print(f'Number of classes: {num_classes} --> {labels}')
Number of classes: 5 --> ['daisy' 'dandelion' 'rose' 'sunflower' 'tulip']
Labels are the 5 species number (from 0 to 4). Thus, we need to encode these labels to one-hot vectors. For instance, an image of a sunflower should have a label 3 and a corresponding y = [0,0,0,1,0].
y = to_categorical(y, num_classes)
print(y.shape)
(4317, 5)
Here, we're gonna split our dataset into a training, a validation and a testing one. This ensures that there are no bias: the model is trained on images with known labels, then we test our model accuracy on the validation dataset on images that our model did not see before. Finally, we compute the accuracy on the test dataset.
#train, validation and test from the train dataset
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, shuffle=True,
test_size=0.25, random_state=28)
Xval, Xtest, yval, ytest = train_test_split(Xtest, ytest, test_size=0.5,
shuffle=True, random_state=28)
print(f'Train dataset: {Xtrain.shape[0]}')
print(f'Validation dataset: {Xval.shape[0]}')
print(f'Test dataset: {Xtest.shape[0]}')
Train dataset: 3237 Validation dataset: 540 Test dataset: 540
#release memory
del X, y
When our model will be built, we need to specify an accuracy function, a loss function and an optimisation algorithm.
The accuracy function is used to evaluate the performance of the model.
The loss function is used to measure how the model performs on data with known labels. It tells us how poorly the model performs in a supersised system. For multi-label classification, we make use of a specific loss function called as categorical_crossentropy (similar to cross-entropy in maths).
Finally, the optimizer function is used in order to minize the loss function by changing model parameters (weighs values, filters kernel values etc.).
For this classification problem, we choose the RMSprop optimizer which is very efficient and commonly used (more details on the optimizers on Keras here).
Since deep networks can take quiet a time for the optimizer to converge, we're gonna use an annealing method of the learning rate (LR).
The LR is basically the step by which the optimizer is 'walking'. A hight LR correspond to big steps and thus the convergence is faster. However, in that case the sampling is not really efficient since the optimizer do not fall especially in the right minima.
At the opposite, have a low LR means that the optimizer will probably find the right local minima but it will take a lot of time.
The idea here is to start from a low value but not so low and then decrease the LR along the training to reach efficiently the global minimum of the loss function. Using the ReduceLROnPlateau method , we are able to choose to reduce the LR by a coefficient (here 75%) if the accuracy has not improved after a number of epochs (here 3).
In addition, we use the EarlyStopping method to control the training time: if the accuracy has not improved after 5 epochs we stop.
Finally we make use of the ModelCheckpoint which is useful for monitoring the best found weights during the training.
vgg16_base_model = VGG16(weights='imagenet', include_top=False)
x = vgg16_base_model.output
x = GlobalAveragePooling2D(name='globalaveragepooling2d')(x) # a global spatial average pooling layer
x = Dense(1024, activation='relu',name='fc1_Dense')(x)
x = Dropout(0.5, name='dropout_1')(x)
x = Dense(1024, activation='relu',name='fc2_Dense')(x)
x = Dropout(0.5, name='dropout_2')(x)
predictions = Dense(5, activation='softmax',name='output_layer')(x)
vgg16_model = Model(inputs=vgg16_base_model.input, outputs=predictions) # this is the model we will train
# frozen the first 15 layers
for layer in vgg16_model.layers[:15]:
layer.trainable = False
for layer in vgg16_model.layers[15:]:
layer.trainable = True
for i, layer in enumerate(vgg16_model.layers):
print(i, layer.name, layer.trainable)
0 input_2 False 1 block1_conv1 False 2 block1_conv2 False 3 block1_pool False 4 block2_conv1 False 5 block2_conv2 False 6 block2_pool False 7 block3_conv1 False 8 block3_conv2 False 9 block3_conv3 False 10 block3_pool False 11 block4_conv1 False 12 block4_conv2 False 13 block4_conv3 False 14 block4_pool False 15 block5_conv1 True 16 block5_conv2 True 17 block5_conv3 True 18 block5_pool True 19 globalaveragepooling2d True 20 fc1_Dense True 21 dropout_1 True 22 fc2_Dense True 23 dropout_2 True 24 output_layer True
# compile the model
sgd = SGD(lr=0.0001, momentum=0.9)
vgg16_model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])
c:\Users\pinku\anaconda3\envs\tf_gpu\lib\site-packages\keras\optimizers\optimizer_v2\gradient_descent.py:108: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead. super(SGD, self).__init__(name, **kwargs)
A useful trick to ovoid any overfitting is to use data augmentation. What is that? Well, the idea is to add artificially data into our dataset. But of course not any data, we alter the dataset with tiny transformations to reproduce very similar images.
For instance, we rotate of a few degree an image, we de-center it or we zoom in or out a little bit. These common augmentation techniques are horizontal/vertical flips, rotations, translations, rescaling, random crops, adjust brightness and more.
Thanks to these transformations, we can get bigger dataset (x2, x3 in size) and then train our model in a much robust way.
image_size = 224
batch_size = 32
path = r'flowers'
#train_gen = train_aug.flow(Xtrain, ytrain, batch_size=batch_size)
# The validation data must not have data augmentation
#valid_gen = valid_no_aug.flow(Xval, yval, batch_size=batch_size)
train_datagen = ImageDataGenerator(
rescale=1./255, # rescale pixel values [0,255] to [0,1]
horizontal_flip=True, # random horizontal flip
width_shift_range=0.2, # random shift images horizontally (fraction of total width)
height_shift_range=0.2, # random shift images vertically (fraction of total height)
zoom_range=0.2) # random zoom image
#rotation_range=20, # random rotation
#shear_range=0.2) # shear transfo
#validation_split=0.2) # splitting train / test datasets
test_datagen = ImageDataGenerator(
rescale=1./255)
#validation_split=0.2)
train_gen = train_datagen.flow(
Xtrain, ytrain,
batch_size=batch_size,
shuffle=False) # already applied
valid_gen = test_datagen.flow(
Xval, yval,
batch_size=batch_size,
shuffle=False)
vgg16_model.summary()
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, None, None, 3)] 0
block1_conv1 (Conv2D) (None, None, None, 64) 1792
block1_conv2 (Conv2D) (None, None, None, 64) 36928
block1_pool (MaxPooling2D) (None, None, None, 64) 0
block2_conv1 (Conv2D) (None, None, None, 128) 73856
block2_conv2 (Conv2D) (None, None, None, 128) 147584
block2_pool (MaxPooling2D) (None, None, None, 128) 0
block3_conv1 (Conv2D) (None, None, None, 256) 295168
block3_conv2 (Conv2D) (None, None, None, 256) 590080
block3_conv3 (Conv2D) (None, None, None, 256) 590080
block3_pool (MaxPooling2D) (None, None, None, 256) 0
block4_conv1 (Conv2D) (None, None, None, 512) 1180160
block4_conv2 (Conv2D) (None, None, None, 512) 2359808
block4_conv3 (Conv2D) (None, None, None, 512) 2359808
block4_pool (MaxPooling2D) (None, None, None, 512) 0
block5_conv1 (Conv2D) (None, None, None, 512) 2359808
block5_conv2 (Conv2D) (None, None, None, 512) 2359808
block5_conv3 (Conv2D) (None, None, None, 512) 2359808
block5_pool (MaxPooling2D) (None, None, None, 512) 0
globalaveragepooling2d (Glo (None, 512) 0
balAveragePooling2D)
fc1_Dense (Dense) (None, 1024) 525312
dropout_1 (Dropout) (None, 1024) 0
fc2_Dense (Dense) (None, 1024) 1049600
dropout_2 (Dropout) (None, 1024) 0
output_layer (Dense) (None, 5) 5125
=================================================================
Total params: 16,294,725
Trainable params: 8,659,461
Non-trainable params: 7,635,264
_________________________________________________________________
batch_size = 32
epochs_0 = 40
steps_per_epoch = len(train_gen.x) // train_gen.batch_size
validation_steps = len(valid_gen.x) // valid_gen.batch_size
history = vgg16_model.fit(
train_gen,
steps_per_epoch=len(Xtrain) // batch_size, # or batch_size=32
epochs=epochs_0 ,
validation_data=valid_gen,
validation_steps=len(Xval) // batch_size)
Epoch 1/40 101/101 [==============================] - 108s 879ms/step - loss: 1.6768 - accuracy: 0.2197 - val_loss: 1.5606 - val_accuracy: 0.3105 Epoch 2/40 101/101 [==============================] - 83s 816ms/step - loss: 1.6241 - accuracy: 0.2465 - val_loss: 1.5199 - val_accuracy: 0.3828 Epoch 3/40 101/101 [==============================] - 84s 830ms/step - loss: 1.5839 - accuracy: 0.2914 - val_loss: 1.4697 - val_accuracy: 0.4414 Epoch 4/40 101/101 [==============================] - 85s 844ms/step - loss: 1.5390 - accuracy: 0.3267 - val_loss: 1.4073 - val_accuracy: 0.4219 Epoch 5/40 101/101 [==============================] - 45s 436ms/step - loss: 1.4804 - accuracy: 0.3719 - val_loss: 1.3277 - val_accuracy: 0.4805 Epoch 6/40 101/101 [==============================] - 42s 417ms/step - loss: 1.4097 - accuracy: 0.4181 - val_loss: 1.2385 - val_accuracy: 0.5215 Epoch 7/40 101/101 [==============================] - 47s 467ms/step - loss: 1.3374 - accuracy: 0.4443 - val_loss: 1.1554 - val_accuracy: 0.6055 Epoch 8/40 101/101 [==============================] - 46s 452ms/step - loss: 1.2584 - accuracy: 0.4846 - val_loss: 1.0665 - val_accuracy: 0.5957 Epoch 9/40 101/101 [==============================] - 39s 389ms/step - loss: 1.1928 - accuracy: 0.5142 - val_loss: 0.9775 - val_accuracy: 0.6562 Epoch 10/40 101/101 [==============================] - 41s 400ms/step - loss: 1.1073 - accuracy: 0.5551 - val_loss: 0.8949 - val_accuracy: 0.7090 Epoch 11/40 101/101 [==============================] - 41s 401ms/step - loss: 1.0459 - accuracy: 0.5850 - val_loss: 0.8372 - val_accuracy: 0.7168 Epoch 12/40 101/101 [==============================] - 40s 398ms/step - loss: 0.9741 - accuracy: 0.6256 - val_loss: 0.7496 - val_accuracy: 0.7441 Epoch 13/40 101/101 [==============================] - 40s 397ms/step - loss: 0.9141 - accuracy: 0.6530 - val_loss: 0.7024 - val_accuracy: 0.7520 Epoch 14/40 101/101 [==============================] - 40s 398ms/step - loss: 0.8468 - accuracy: 0.6761 - val_loss: 0.6546 - val_accuracy: 0.7598 Epoch 15/40 101/101 [==============================] - 41s 402ms/step - loss: 0.8070 - accuracy: 0.6917 - val_loss: 0.6416 - val_accuracy: 0.7715 Epoch 16/40 101/101 [==============================] - 42s 410ms/step - loss: 0.7460 - accuracy: 0.7173 - val_loss: 0.5985 - val_accuracy: 0.7812 Epoch 17/40 101/101 [==============================] - 42s 409ms/step - loss: 0.7239 - accuracy: 0.7323 - val_loss: 0.5849 - val_accuracy: 0.7871 Epoch 18/40 101/101 [==============================] - 42s 412ms/step - loss: 0.6971 - accuracy: 0.7482 - val_loss: 0.5712 - val_accuracy: 0.7891 Epoch 19/40 101/101 [==============================] - 42s 412ms/step - loss: 0.6682 - accuracy: 0.7573 - val_loss: 0.5361 - val_accuracy: 0.8086 Epoch 20/40 101/101 [==============================] - 43s 422ms/step - loss: 0.6474 - accuracy: 0.7557 - val_loss: 0.5299 - val_accuracy: 0.8145 Epoch 21/40 101/101 [==============================] - 42s 410ms/step - loss: 0.6122 - accuracy: 0.7722 - val_loss: 0.5197 - val_accuracy: 0.8164 Epoch 22/40 101/101 [==============================] - 40s 395ms/step - loss: 0.5903 - accuracy: 0.7832 - val_loss: 0.5177 - val_accuracy: 0.8242 Epoch 23/40 101/101 [==============================] - 41s 402ms/step - loss: 0.5819 - accuracy: 0.7897 - val_loss: 0.5184 - val_accuracy: 0.8184 Epoch 24/40 101/101 [==============================] - 40s 399ms/step - loss: 0.5653 - accuracy: 0.7906 - val_loss: 0.5207 - val_accuracy: 0.8203 Epoch 25/40 101/101 [==============================] - 40s 396ms/step - loss: 0.5594 - accuracy: 0.7928 - val_loss: 0.4877 - val_accuracy: 0.8320 Epoch 26/40 101/101 [==============================] - 41s 401ms/step - loss: 0.5463 - accuracy: 0.8047 - val_loss: 0.4670 - val_accuracy: 0.8262 Epoch 27/40 101/101 [==============================] - 40s 397ms/step - loss: 0.5289 - accuracy: 0.8103 - val_loss: 0.4839 - val_accuracy: 0.8242 Epoch 28/40 101/101 [==============================] - 41s 401ms/step - loss: 0.5113 - accuracy: 0.8159 - val_loss: 0.4832 - val_accuracy: 0.8418 Epoch 29/40 101/101 [==============================] - 41s 406ms/step - loss: 0.5033 - accuracy: 0.8178 - val_loss: 0.4561 - val_accuracy: 0.8438 Epoch 30/40 101/101 [==============================] - 41s 400ms/step - loss: 0.4932 - accuracy: 0.8225 - val_loss: 0.4559 - val_accuracy: 0.8457 Epoch 31/40 101/101 [==============================] - 40s 393ms/step - loss: 0.4784 - accuracy: 0.8281 - val_loss: 0.4763 - val_accuracy: 0.8398 Epoch 32/40 101/101 [==============================] - 41s 401ms/step - loss: 0.4831 - accuracy: 0.8253 - val_loss: 0.4499 - val_accuracy: 0.8516 Epoch 33/40 101/101 [==============================] - 40s 398ms/step - loss: 0.4587 - accuracy: 0.8359 - val_loss: 0.4526 - val_accuracy: 0.8535 Epoch 34/40 101/101 [==============================] - 41s 407ms/step - loss: 0.4654 - accuracy: 0.8290 - val_loss: 0.4774 - val_accuracy: 0.8340 Epoch 35/40 101/101 [==============================] - 41s 402ms/step - loss: 0.4591 - accuracy: 0.8449 - val_loss: 0.4328 - val_accuracy: 0.8574 Epoch 36/40 101/101 [==============================] - 40s 399ms/step - loss: 0.4493 - accuracy: 0.8368 - val_loss: 0.4214 - val_accuracy: 0.8438 Epoch 37/40 101/101 [==============================] - 41s 402ms/step - loss: 0.4464 - accuracy: 0.8346 - val_loss: 0.4293 - val_accuracy: 0.8594 Epoch 38/40 101/101 [==============================] - 40s 399ms/step - loss: 0.4349 - accuracy: 0.8418 - val_loss: 0.4043 - val_accuracy: 0.8613 Epoch 39/40 101/101 [==============================] - 41s 405ms/step - loss: 0.4334 - accuracy: 0.8474 - val_loss: 0.4067 - val_accuracy: 0.8672 Epoch 40/40 101/101 [==============================] - 40s 399ms/step - loss: 0.4169 - accuracy: 0.8518 - val_loss: 0.4219 - val_accuracy: 0.8496
# Generator for test dataset
vgg16_datagen = ImageDataGenerator(
rescale=1./255)
eval_datagen = vgg16_datagen.flow(
Xtest, ytest,
batch_size=batch_size,
shuffle=False) # since shuffle was already during splitting into train, valid, test
# Evaluation on the test dataset
loss, acc = vgg16_model.evaluate(eval_datagen, verbose=0)
print(f'Test loss: {loss:.2f}')
print(f'Test accuracy: {acc*100:.2f}%')
Test loss: 0.44 Test accuracy: 85.00%
def plot_history(history, loss_max=5):
"""
Check loss and accuracy evolution.
"""
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
fig, (ax1, ax2) = plt.subplots(1,2,figsize=(14, 4))
ax1.plot(acc, label='Training')
ax1.plot(val_acc, label='Validation')
ax1.legend(loc='lower right')
ax1.set(ylabel='Accuracy', title='Training - Validation Accuracy',
ylim=([min(plt.ylim()),1]))
ax2.plot(loss, label='Training')
ax2.plot(val_loss, label='Validation')
ax2.legend(loc='upper right')
ax2.set(ylabel='Loss (cross entropy)', xlabel='epochs',
title='Training - Validation Loss', ylim=([0, loss_max]))
plt.show()
plot_history(history, loss_max=1)
import seaborn as sns
from sklearn import metrics
pred = vgg16_model.predict(eval_datagen, verbose=1)
# get most likely class
y_pred = pred.argmax(axis=1)
y_true = ytest.argmax(axis=1)
print(metrics.classification_report(y_true, y_pred))
# confusion matrix
mat = metrics.confusion_matrix(y_true, y_pred)
df_mat = pd.DataFrame(mat, index=labels, columns=labels)
plt.figure(figsize=(8,6))
sns.heatmap(df_mat, annot=True, fmt='d', cmap=plt.cm.Reds)
#plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
17/17 [==============================] - 4s 208ms/step
precision recall f1-score support
0 0.93 0.81 0.86 99
1 0.88 0.91 0.89 128
2 0.74 0.91 0.81 99
3 0.87 0.88 0.87 88
4 0.86 0.76 0.81 126
accuracy 0.85 540
macro avg 0.86 0.85 0.85 540
weighted avg 0.86 0.85 0.85 540
Text(0.5, 36.72222222222221, 'Predicted label')
N = 20 # flowers to display
fig, axes = plt.subplots(4, 5, figsize=(20,12))
for i, ax in enumerate(axes.flat):
ax.imshow(Xtest[i].astype(np.uint8))
ax.set(xticks=[], yticks=[])
true = y_true[i]
prediction = y_pred[i]
ax.set_xlabel(f'Predict: {labels[prediction]}\n True: {labels[true]}',
color='black' if true == prediction else 'red')
#fig.tight_layout()
fig.suptitle('Predicted flowers; Incorrect Labels in Red', size=14)
Text(0.5, 0.98, 'Predicted flowers; Incorrect Labels in Red')
# Import the Xception model to use as the base for our model
xception_base = xception.Xception(
include_top=False,
weights='imagenet'
)
x = xception_base.output
# Global averaging pool layer
x = GlobalAveragePooling2D()(x)
# Regular densely connected layer
x = Dense(512, activation='relu')(x)
# Output layer
predictions = Dense(5, activation='softmax')(x)
xception_model = Model(inputs=xception_base.input, outputs=predictions)
def setup_model(model, trainable):
# Freeze the un-trainable layers of the model base
for layer in model.layers[:(len(model.layers) - trainable)]:
layer.trainable = False
for layer in model.layers[(len(model.layers) - trainable):]:
layer.trainable = True
model.compile(
loss='categorical_crossentropy',
# Slower training rate for fine-tuning
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy']
)
setup_model(xception_model, 19)
c:\Users\pinku\anaconda3\envs\tf_gpu\lib\site-packages\keras\optimizers\optimizer_v2\gradient_descent.py:108: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead. super(SGD, self).__init__(name, **kwargs)
for i, layer in enumerate(xception_model.layers):
print(i, layer.name, layer.trainable)
0 input_3 False 1 block1_conv1 False 2 block1_conv1_bn False 3 block1_conv1_act False 4 block1_conv2 False 5 block1_conv2_bn False 6 block1_conv2_act False 7 block2_sepconv1 False 8 block2_sepconv1_bn False 9 block2_sepconv2_act False 10 block2_sepconv2 False 11 block2_sepconv2_bn False 12 conv2d False 13 block2_pool False 14 batch_normalization False 15 add False 16 block3_sepconv1_act False 17 block3_sepconv1 False 18 block3_sepconv1_bn False 19 block3_sepconv2_act False 20 block3_sepconv2 False 21 block3_sepconv2_bn False 22 conv2d_1 False 23 block3_pool False 24 batch_normalization_1 False 25 add_1 False 26 block4_sepconv1_act False 27 block4_sepconv1 False 28 block4_sepconv1_bn False 29 block4_sepconv2_act False 30 block4_sepconv2 False 31 block4_sepconv2_bn False 32 conv2d_2 False 33 block4_pool False 34 batch_normalization_2 False 35 add_2 False 36 block5_sepconv1_act False 37 block5_sepconv1 False 38 block5_sepconv1_bn False 39 block5_sepconv2_act False 40 block5_sepconv2 False 41 block5_sepconv2_bn False 42 block5_sepconv3_act False 43 block5_sepconv3 False 44 block5_sepconv3_bn False 45 add_3 False 46 block6_sepconv1_act False 47 block6_sepconv1 False 48 block6_sepconv1_bn False 49 block6_sepconv2_act False 50 block6_sepconv2 False 51 block6_sepconv2_bn False 52 block6_sepconv3_act False 53 block6_sepconv3 False 54 block6_sepconv3_bn False 55 add_4 False 56 block7_sepconv1_act False 57 block7_sepconv1 False 58 block7_sepconv1_bn False 59 block7_sepconv2_act False 60 block7_sepconv2 False 61 block7_sepconv2_bn False 62 block7_sepconv3_act False 63 block7_sepconv3 False 64 block7_sepconv3_bn False 65 add_5 False 66 block8_sepconv1_act False 67 block8_sepconv1 False 68 block8_sepconv1_bn False 69 block8_sepconv2_act False 70 block8_sepconv2 False 71 block8_sepconv2_bn False 72 block8_sepconv3_act False 73 block8_sepconv3 False 74 block8_sepconv3_bn False 75 add_6 False 76 block9_sepconv1_act False 77 block9_sepconv1 False 78 block9_sepconv1_bn False 79 block9_sepconv2_act False 80 block9_sepconv2 False 81 block9_sepconv2_bn False 82 block9_sepconv3_act False 83 block9_sepconv3 False 84 block9_sepconv3_bn False 85 add_7 False 86 block10_sepconv1_act False 87 block10_sepconv1 False 88 block10_sepconv1_bn False 89 block10_sepconv2_act False 90 block10_sepconv2 False 91 block10_sepconv2_bn False 92 block10_sepconv3_act False 93 block10_sepconv3 False 94 block10_sepconv3_bn False 95 add_8 False 96 block11_sepconv1_act False 97 block11_sepconv1 False 98 block11_sepconv1_bn False 99 block11_sepconv2_act False 100 block11_sepconv2 False 101 block11_sepconv2_bn False 102 block11_sepconv3_act False 103 block11_sepconv3 False 104 block11_sepconv3_bn False 105 add_9 False 106 block12_sepconv1_act False 107 block12_sepconv1 False 108 block12_sepconv1_bn False 109 block12_sepconv2_act False 110 block12_sepconv2 False 111 block12_sepconv2_bn False 112 block12_sepconv3_act False 113 block12_sepconv3 False 114 block12_sepconv3_bn False 115 add_10 False 116 block13_sepconv1_act True 117 block13_sepconv1 True 118 block13_sepconv1_bn True 119 block13_sepconv2_act True 120 block13_sepconv2 True 121 block13_sepconv2_bn True 122 conv2d_3 True 123 block13_pool True 124 batch_normalization_3 True 125 add_11 True 126 block14_sepconv1 True 127 block14_sepconv1_bn True 128 block14_sepconv1_act True 129 block14_sepconv2 True 130 block14_sepconv2_bn True 131 block14_sepconv2_act True 132 global_average_pooling2d True 133 dense True 134 dense_1 True
xception_model.summary()
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_3 (InputLayer) [(None, None, None, 0 []
3)]
block1_conv1 (Conv2D) (None, None, None, 864 ['input_3[0][0]']
32)
block1_conv1_bn (BatchNormaliz (None, None, None, 128 ['block1_conv1[0][0]']
ation) 32)
block1_conv1_act (Activation) (None, None, None, 0 ['block1_conv1_bn[0][0]']
32)
block1_conv2 (Conv2D) (None, None, None, 18432 ['block1_conv1_act[0][0]']
64)
block1_conv2_bn (BatchNormaliz (None, None, None, 256 ['block1_conv2[0][0]']
ation) 64)
block1_conv2_act (Activation) (None, None, None, 0 ['block1_conv2_bn[0][0]']
64)
block2_sepconv1 (SeparableConv (None, None, None, 8768 ['block1_conv2_act[0][0]']
2D) 128)
block2_sepconv1_bn (BatchNorma (None, None, None, 512 ['block2_sepconv1[0][0]']
lization) 128)
block2_sepconv2_act (Activatio (None, None, None, 0 ['block2_sepconv1_bn[0][0]']
n) 128)
block2_sepconv2 (SeparableConv (None, None, None, 17536 ['block2_sepconv2_act[0][0]']
2D) 128)
block2_sepconv2_bn (BatchNorma (None, None, None, 512 ['block2_sepconv2[0][0]']
lization) 128)
conv2d (Conv2D) (None, None, None, 8192 ['block1_conv2_act[0][0]']
128)
block2_pool (MaxPooling2D) (None, None, None, 0 ['block2_sepconv2_bn[0][0]']
128)
batch_normalization (BatchNorm (None, None, None, 512 ['conv2d[0][0]']
alization) 128)
add (Add) (None, None, None, 0 ['block2_pool[0][0]',
128) 'batch_normalization[0][0]']
block3_sepconv1_act (Activatio (None, None, None, 0 ['add[0][0]']
n) 128)
block3_sepconv1 (SeparableConv (None, None, None, 33920 ['block3_sepconv1_act[0][0]']
2D) 256)
block3_sepconv1_bn (BatchNorma (None, None, None, 1024 ['block3_sepconv1[0][0]']
lization) 256)
block3_sepconv2_act (Activatio (None, None, None, 0 ['block3_sepconv1_bn[0][0]']
n) 256)
block3_sepconv2 (SeparableConv (None, None, None, 67840 ['block3_sepconv2_act[0][0]']
2D) 256)
block3_sepconv2_bn (BatchNorma (None, None, None, 1024 ['block3_sepconv2[0][0]']
lization) 256)
conv2d_1 (Conv2D) (None, None, None, 32768 ['add[0][0]']
256)
block3_pool (MaxPooling2D) (None, None, None, 0 ['block3_sepconv2_bn[0][0]']
256)
batch_normalization_1 (BatchNo (None, None, None, 1024 ['conv2d_1[0][0]']
rmalization) 256)
add_1 (Add) (None, None, None, 0 ['block3_pool[0][0]',
256) 'batch_normalization_1[0][0]']
block4_sepconv1_act (Activatio (None, None, None, 0 ['add_1[0][0]']
n) 256)
block4_sepconv1 (SeparableConv (None, None, None, 188672 ['block4_sepconv1_act[0][0]']
2D) 728)
block4_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block4_sepconv1[0][0]']
lization) 728)
block4_sepconv2_act (Activatio (None, None, None, 0 ['block4_sepconv1_bn[0][0]']
n) 728)
block4_sepconv2 (SeparableConv (None, None, None, 536536 ['block4_sepconv2_act[0][0]']
2D) 728)
block4_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block4_sepconv2[0][0]']
lization) 728)
conv2d_2 (Conv2D) (None, None, None, 186368 ['add_1[0][0]']
728)
block4_pool (MaxPooling2D) (None, None, None, 0 ['block4_sepconv2_bn[0][0]']
728)
batch_normalization_2 (BatchNo (None, None, None, 2912 ['conv2d_2[0][0]']
rmalization) 728)
add_2 (Add) (None, None, None, 0 ['block4_pool[0][0]',
728) 'batch_normalization_2[0][0]']
block5_sepconv1_act (Activatio (None, None, None, 0 ['add_2[0][0]']
n) 728)
block5_sepconv1 (SeparableConv (None, None, None, 536536 ['block5_sepconv1_act[0][0]']
2D) 728)
block5_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block5_sepconv1[0][0]']
lization) 728)
block5_sepconv2_act (Activatio (None, None, None, 0 ['block5_sepconv1_bn[0][0]']
n) 728)
block5_sepconv2 (SeparableConv (None, None, None, 536536 ['block5_sepconv2_act[0][0]']
2D) 728)
block5_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block5_sepconv2[0][0]']
lization) 728)
block5_sepconv3_act (Activatio (None, None, None, 0 ['block5_sepconv2_bn[0][0]']
n) 728)
block5_sepconv3 (SeparableConv (None, None, None, 536536 ['block5_sepconv3_act[0][0]']
2D) 728)
block5_sepconv3_bn (BatchNorma (None, None, None, 2912 ['block5_sepconv3[0][0]']
lization) 728)
add_3 (Add) (None, None, None, 0 ['block5_sepconv3_bn[0][0]',
728) 'add_2[0][0]']
block6_sepconv1_act (Activatio (None, None, None, 0 ['add_3[0][0]']
n) 728)
block6_sepconv1 (SeparableConv (None, None, None, 536536 ['block6_sepconv1_act[0][0]']
2D) 728)
block6_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block6_sepconv1[0][0]']
lization) 728)
block6_sepconv2_act (Activatio (None, None, None, 0 ['block6_sepconv1_bn[0][0]']
n) 728)
block6_sepconv2 (SeparableConv (None, None, None, 536536 ['block6_sepconv2_act[0][0]']
2D) 728)
block6_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block6_sepconv2[0][0]']
lization) 728)
block6_sepconv3_act (Activatio (None, None, None, 0 ['block6_sepconv2_bn[0][0]']
n) 728)
block6_sepconv3 (SeparableConv (None, None, None, 536536 ['block6_sepconv3_act[0][0]']
2D) 728)
block6_sepconv3_bn (BatchNorma (None, None, None, 2912 ['block6_sepconv3[0][0]']
lization) 728)
add_4 (Add) (None, None, None, 0 ['block6_sepconv3_bn[0][0]',
728) 'add_3[0][0]']
block7_sepconv1_act (Activatio (None, None, None, 0 ['add_4[0][0]']
n) 728)
block7_sepconv1 (SeparableConv (None, None, None, 536536 ['block7_sepconv1_act[0][0]']
2D) 728)
block7_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block7_sepconv1[0][0]']
lization) 728)
block7_sepconv2_act (Activatio (None, None, None, 0 ['block7_sepconv1_bn[0][0]']
n) 728)
block7_sepconv2 (SeparableConv (None, None, None, 536536 ['block7_sepconv2_act[0][0]']
2D) 728)
block7_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block7_sepconv2[0][0]']
lization) 728)
block7_sepconv3_act (Activatio (None, None, None, 0 ['block7_sepconv2_bn[0][0]']
n) 728)
block7_sepconv3 (SeparableConv (None, None, None, 536536 ['block7_sepconv3_act[0][0]']
2D) 728)
block7_sepconv3_bn (BatchNorma (None, None, None, 2912 ['block7_sepconv3[0][0]']
lization) 728)
add_5 (Add) (None, None, None, 0 ['block7_sepconv3_bn[0][0]',
728) 'add_4[0][0]']
block8_sepconv1_act (Activatio (None, None, None, 0 ['add_5[0][0]']
n) 728)
block8_sepconv1 (SeparableConv (None, None, None, 536536 ['block8_sepconv1_act[0][0]']
2D) 728)
block8_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block8_sepconv1[0][0]']
lization) 728)
block8_sepconv2_act (Activatio (None, None, None, 0 ['block8_sepconv1_bn[0][0]']
n) 728)
block8_sepconv2 (SeparableConv (None, None, None, 536536 ['block8_sepconv2_act[0][0]']
2D) 728)
block8_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block8_sepconv2[0][0]']
lization) 728)
block8_sepconv3_act (Activatio (None, None, None, 0 ['block8_sepconv2_bn[0][0]']
n) 728)
block8_sepconv3 (SeparableConv (None, None, None, 536536 ['block8_sepconv3_act[0][0]']
2D) 728)
block8_sepconv3_bn (BatchNorma (None, None, None, 2912 ['block8_sepconv3[0][0]']
lization) 728)
add_6 (Add) (None, None, None, 0 ['block8_sepconv3_bn[0][0]',
728) 'add_5[0][0]']
block9_sepconv1_act (Activatio (None, None, None, 0 ['add_6[0][0]']
n) 728)
block9_sepconv1 (SeparableConv (None, None, None, 536536 ['block9_sepconv1_act[0][0]']
2D) 728)
block9_sepconv1_bn (BatchNorma (None, None, None, 2912 ['block9_sepconv1[0][0]']
lization) 728)
block9_sepconv2_act (Activatio (None, None, None, 0 ['block9_sepconv1_bn[0][0]']
n) 728)
block9_sepconv2 (SeparableConv (None, None, None, 536536 ['block9_sepconv2_act[0][0]']
2D) 728)
block9_sepconv2_bn (BatchNorma (None, None, None, 2912 ['block9_sepconv2[0][0]']
lization) 728)
block9_sepconv3_act (Activatio (None, None, None, 0 ['block9_sepconv2_bn[0][0]']
n) 728)
block9_sepconv3 (SeparableConv (None, None, None, 536536 ['block9_sepconv3_act[0][0]']
2D) 728)
block9_sepconv3_bn (BatchNorma (None, None, None, 2912 ['block9_sepconv3[0][0]']
lization) 728)
add_7 (Add) (None, None, None, 0 ['block9_sepconv3_bn[0][0]',
728) 'add_6[0][0]']
block10_sepconv1_act (Activati (None, None, None, 0 ['add_7[0][0]']
on) 728)
block10_sepconv1 (SeparableCon (None, None, None, 536536 ['block10_sepconv1_act[0][0]']
v2D) 728)
block10_sepconv1_bn (BatchNorm (None, None, None, 2912 ['block10_sepconv1[0][0]']
alization) 728)
block10_sepconv2_act (Activati (None, None, None, 0 ['block10_sepconv1_bn[0][0]']
on) 728)
block10_sepconv2 (SeparableCon (None, None, None, 536536 ['block10_sepconv2_act[0][0]']
v2D) 728)
block10_sepconv2_bn (BatchNorm (None, None, None, 2912 ['block10_sepconv2[0][0]']
alization) 728)
block10_sepconv3_act (Activati (None, None, None, 0 ['block10_sepconv2_bn[0][0]']
on) 728)
block10_sepconv3 (SeparableCon (None, None, None, 536536 ['block10_sepconv3_act[0][0]']
v2D) 728)
block10_sepconv3_bn (BatchNorm (None, None, None, 2912 ['block10_sepconv3[0][0]']
alization) 728)
add_8 (Add) (None, None, None, 0 ['block10_sepconv3_bn[0][0]',
728) 'add_7[0][0]']
block11_sepconv1_act (Activati (None, None, None, 0 ['add_8[0][0]']
on) 728)
block11_sepconv1 (SeparableCon (None, None, None, 536536 ['block11_sepconv1_act[0][0]']
v2D) 728)
block11_sepconv1_bn (BatchNorm (None, None, None, 2912 ['block11_sepconv1[0][0]']
alization) 728)
block11_sepconv2_act (Activati (None, None, None, 0 ['block11_sepconv1_bn[0][0]']
on) 728)
block11_sepconv2 (SeparableCon (None, None, None, 536536 ['block11_sepconv2_act[0][0]']
v2D) 728)
block11_sepconv2_bn (BatchNorm (None, None, None, 2912 ['block11_sepconv2[0][0]']
alization) 728)
block11_sepconv3_act (Activati (None, None, None, 0 ['block11_sepconv2_bn[0][0]']
on) 728)
block11_sepconv3 (SeparableCon (None, None, None, 536536 ['block11_sepconv3_act[0][0]']
v2D) 728)
block11_sepconv3_bn (BatchNorm (None, None, None, 2912 ['block11_sepconv3[0][0]']
alization) 728)
add_9 (Add) (None, None, None, 0 ['block11_sepconv3_bn[0][0]',
728) 'add_8[0][0]']
block12_sepconv1_act (Activati (None, None, None, 0 ['add_9[0][0]']
on) 728)
block12_sepconv1 (SeparableCon (None, None, None, 536536 ['block12_sepconv1_act[0][0]']
v2D) 728)
block12_sepconv1_bn (BatchNorm (None, None, None, 2912 ['block12_sepconv1[0][0]']
alization) 728)
block12_sepconv2_act (Activati (None, None, None, 0 ['block12_sepconv1_bn[0][0]']
on) 728)
block12_sepconv2 (SeparableCon (None, None, None, 536536 ['block12_sepconv2_act[0][0]']
v2D) 728)
block12_sepconv2_bn (BatchNorm (None, None, None, 2912 ['block12_sepconv2[0][0]']
alization) 728)
block12_sepconv3_act (Activati (None, None, None, 0 ['block12_sepconv2_bn[0][0]']
on) 728)
block12_sepconv3 (SeparableCon (None, None, None, 536536 ['block12_sepconv3_act[0][0]']
v2D) 728)
block12_sepconv3_bn (BatchNorm (None, None, None, 2912 ['block12_sepconv3[0][0]']
alization) 728)
add_10 (Add) (None, None, None, 0 ['block12_sepconv3_bn[0][0]',
728) 'add_9[0][0]']
block13_sepconv1_act (Activati (None, None, None, 0 ['add_10[0][0]']
on) 728)
block13_sepconv1 (SeparableCon (None, None, None, 536536 ['block13_sepconv1_act[0][0]']
v2D) 728)
block13_sepconv1_bn (BatchNorm (None, None, None, 2912 ['block13_sepconv1[0][0]']
alization) 728)
block13_sepconv2_act (Activati (None, None, None, 0 ['block13_sepconv1_bn[0][0]']
on) 728)
block13_sepconv2 (SeparableCon (None, None, None, 752024 ['block13_sepconv2_act[0][0]']
v2D) 1024)
block13_sepconv2_bn (BatchNorm (None, None, None, 4096 ['block13_sepconv2[0][0]']
alization) 1024)
conv2d_3 (Conv2D) (None, None, None, 745472 ['add_10[0][0]']
1024)
block13_pool (MaxPooling2D) (None, None, None, 0 ['block13_sepconv2_bn[0][0]']
1024)
batch_normalization_3 (BatchNo (None, None, None, 4096 ['conv2d_3[0][0]']
rmalization) 1024)
add_11 (Add) (None, None, None, 0 ['block13_pool[0][0]',
1024) 'batch_normalization_3[0][0]']
block14_sepconv1 (SeparableCon (None, None, None, 1582080 ['add_11[0][0]']
v2D) 1536)
block14_sepconv1_bn (BatchNorm (None, None, None, 6144 ['block14_sepconv1[0][0]']
alization) 1536)
block14_sepconv1_act (Activati (None, None, None, 0 ['block14_sepconv1_bn[0][0]']
on) 1536)
block14_sepconv2 (SeparableCon (None, None, None, 3159552 ['block14_sepconv1_act[0][0]']
v2D) 2048)
block14_sepconv2_bn (BatchNorm (None, None, None, 8192 ['block14_sepconv2[0][0]']
alization) 2048)
block14_sepconv2_act (Activati (None, None, None, 0 ['block14_sepconv2_bn[0][0]']
on) 2048)
global_average_pooling2d (Glob (None, 2048) 0 ['block14_sepconv2_act[0][0]']
alAveragePooling2D)
dense (Dense) (None, 512) 1049088 ['global_average_pooling2d[0][0]'
]
dense_1 (Dense) (None, 5) 2565 ['dense[0][0]']
==================================================================================================
Total params: 21,913,133
Trainable params: 7,840,037
Non-trainable params: 14,073,096
__________________________________________________________________________________________________
batch_size = 32
epochs_0 = 40
steps_per_epoch = len(train_gen.x) // train_gen.batch_size
validation_steps = len(valid_gen.x) // valid_gen.batch_size
history_2 = xception_model.fit(
train_gen,
steps_per_epoch=len(Xtrain) // batch_size, # or batch_size=32
epochs=epochs_0 ,
validation_data=valid_gen,
validation_steps=len(Xval) // batch_size)
Epoch 1/40 101/101 [==============================] - 38s 337ms/step - loss: 1.6074 - accuracy: 0.2477 - val_loss: 1.5291 - val_accuracy: 0.3672 Epoch 2/40 101/101 [==============================] - 32s 312ms/step - loss: 1.5303 - accuracy: 0.3850 - val_loss: 1.4275 - val_accuracy: 0.4707 Epoch 3/40 101/101 [==============================] - 31s 311ms/step - loss: 1.4612 - accuracy: 0.4949 - val_loss: 1.3546 - val_accuracy: 0.5488 Epoch 4/40 101/101 [==============================] - 32s 313ms/step - loss: 1.3992 - accuracy: 0.5304 - val_loss: 1.3048 - val_accuracy: 0.5996 Epoch 5/40 101/101 [==============================] - 32s 315ms/step - loss: 1.3404 - accuracy: 0.5676 - val_loss: 1.2570 - val_accuracy: 0.6367 Epoch 6/40 101/101 [==============================] - 32s 314ms/step - loss: 1.2798 - accuracy: 0.6066 - val_loss: 1.2002 - val_accuracy: 0.6543 Epoch 7/40 101/101 [==============================] - 32s 315ms/step - loss: 1.2202 - accuracy: 0.6427 - val_loss: 1.1447 - val_accuracy: 0.6738 Epoch 8/40 101/101 [==============================] - 35s 348ms/step - loss: 1.1695 - accuracy: 0.6512 - val_loss: 1.0854 - val_accuracy: 0.6914 Epoch 9/40 101/101 [==============================] - 41s 408ms/step - loss: 1.1111 - accuracy: 0.6758 - val_loss: 1.0327 - val_accuracy: 0.7109 Epoch 10/40 101/101 [==============================] - 34s 340ms/step - loss: 1.0600 - accuracy: 0.7023 - val_loss: 0.9812 - val_accuracy: 0.7207 Epoch 11/40 101/101 [==============================] - 34s 336ms/step - loss: 1.0102 - accuracy: 0.7317 - val_loss: 0.9362 - val_accuracy: 0.7383 Epoch 12/40 101/101 [==============================] - 36s 351ms/step - loss: 0.9709 - accuracy: 0.7392 - val_loss: 0.8921 - val_accuracy: 0.7461 Epoch 13/40 101/101 [==============================] - 35s 342ms/step - loss: 0.9263 - accuracy: 0.7460 - val_loss: 0.8509 - val_accuracy: 0.7617 Epoch 14/40 101/101 [==============================] - 35s 341ms/step - loss: 0.8883 - accuracy: 0.7616 - val_loss: 0.8148 - val_accuracy: 0.7695 Epoch 15/40 101/101 [==============================] - 35s 342ms/step - loss: 0.8520 - accuracy: 0.7738 - val_loss: 0.7796 - val_accuracy: 0.7930 Epoch 16/40 101/101 [==============================] - 34s 338ms/step - loss: 0.8255 - accuracy: 0.7785 - val_loss: 0.7479 - val_accuracy: 0.7988 Epoch 17/40 101/101 [==============================] - 35s 344ms/step - loss: 0.8010 - accuracy: 0.7769 - val_loss: 0.7190 - val_accuracy: 0.8086 Epoch 18/40 101/101 [==============================] - 35s 343ms/step - loss: 0.7697 - accuracy: 0.7803 - val_loss: 0.6922 - val_accuracy: 0.8184 Epoch 19/40 101/101 [==============================] - 35s 347ms/step - loss: 0.7444 - accuracy: 0.7878 - val_loss: 0.6707 - val_accuracy: 0.8242 Epoch 20/40 101/101 [==============================] - 35s 349ms/step - loss: 0.7184 - accuracy: 0.7913 - val_loss: 0.6470 - val_accuracy: 0.8281 Epoch 21/40 101/101 [==============================] - 36s 351ms/step - loss: 0.6912 - accuracy: 0.8031 - val_loss: 0.6271 - val_accuracy: 0.8340 Epoch 22/40 101/101 [==============================] - 35s 347ms/step - loss: 0.6698 - accuracy: 0.8109 - val_loss: 0.6088 - val_accuracy: 0.8340 Epoch 23/40 101/101 [==============================] - 35s 349ms/step - loss: 0.6507 - accuracy: 0.8100 - val_loss: 0.5923 - val_accuracy: 0.8418 Epoch 24/40 101/101 [==============================] - 35s 349ms/step - loss: 0.6358 - accuracy: 0.8109 - val_loss: 0.5756 - val_accuracy: 0.8457 Epoch 25/40 101/101 [==============================] - 35s 347ms/step - loss: 0.6173 - accuracy: 0.8150 - val_loss: 0.5567 - val_accuracy: 0.8516 Epoch 26/40 101/101 [==============================] - 35s 346ms/step - loss: 0.6073 - accuracy: 0.8200 - val_loss: 0.5433 - val_accuracy: 0.8594 Epoch 27/40 101/101 [==============================] - 36s 351ms/step - loss: 0.5862 - accuracy: 0.8293 - val_loss: 0.5320 - val_accuracy: 0.8594 Epoch 28/40 101/101 [==============================] - 35s 350ms/step - loss: 0.5781 - accuracy: 0.8212 - val_loss: 0.5204 - val_accuracy: 0.8594 Epoch 29/40 101/101 [==============================] - 36s 355ms/step - loss: 0.5688 - accuracy: 0.8225 - val_loss: 0.5071 - val_accuracy: 0.8594 Epoch 30/40 101/101 [==============================] - 35s 350ms/step - loss: 0.5530 - accuracy: 0.8215 - val_loss: 0.4962 - val_accuracy: 0.8613 Epoch 31/40 101/101 [==============================] - 35s 350ms/step - loss: 0.5397 - accuracy: 0.8343 - val_loss: 0.4875 - val_accuracy: 0.8594 Epoch 32/40 101/101 [==============================] - 35s 350ms/step - loss: 0.5277 - accuracy: 0.8309 - val_loss: 0.4766 - val_accuracy: 0.8613 Epoch 33/40 101/101 [==============================] - 36s 352ms/step - loss: 0.5269 - accuracy: 0.8312 - val_loss: 0.4693 - val_accuracy: 0.8613 Epoch 34/40 101/101 [==============================] - 41s 403ms/step - loss: 0.5102 - accuracy: 0.8365 - val_loss: 0.4616 - val_accuracy: 0.8652 Epoch 35/40 101/101 [==============================] - 41s 404ms/step - loss: 0.4978 - accuracy: 0.8381 - val_loss: 0.4549 - val_accuracy: 0.8652 Epoch 36/40 101/101 [==============================] - 42s 419ms/step - loss: 0.4932 - accuracy: 0.8359 - val_loss: 0.4461 - val_accuracy: 0.8730 Epoch 37/40 101/101 [==============================] - 35s 343ms/step - loss: 0.4867 - accuracy: 0.8452 - val_loss: 0.4393 - val_accuracy: 0.8730 Epoch 38/40 101/101 [==============================] - 35s 341ms/step - loss: 0.4829 - accuracy: 0.8349 - val_loss: 0.4314 - val_accuracy: 0.8750 Epoch 39/40 101/101 [==============================] - 34s 335ms/step - loss: 0.4632 - accuracy: 0.8496 - val_loss: 0.4251 - val_accuracy: 0.8770 Epoch 40/40 101/101 [==============================] - 35s 340ms/step - loss: 0.4623 - accuracy: 0.8490 - val_loss: 0.4219 - val_accuracy: 0.8750
xception_datagen = ImageDataGenerator(
rescale=1./255)
eval_datagen = xception_datagen.flow(
Xtest, ytest,
batch_size=batch_size,
shuffle=False) # since shuffle was already during splitting into train, valid, test
# Evaluation on the test dataset
loss, acc = xception_model.evaluate(eval_datagen, verbose=0)
print(f'Test loss: {loss:.2f}')
print(f'Test accuracy: {acc*100:.2f}%')
Test loss: 0.42 Test accuracy: 84.26%
def plot_history(history, loss_max=5):
"""
Check loss and accuracy evolution.
"""
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
fig, (ax1, ax2) = plt.subplots(1,2,figsize=(14, 4))
ax1.plot(acc, label='Training')
ax1.plot(val_acc, label='Validation')
ax1.legend(loc='lower right')
ax1.set(ylabel='Accuracy', title='Training - Validation Accuracy',
ylim=([min(plt.ylim()),1]))
ax2.plot(loss, label='Training')
ax2.plot(val_loss, label='Validation')
ax2.legend(loc='upper right')
ax2.set(ylabel='Loss (cross entropy)', xlabel='epochs',
title='Training - Validation Loss', ylim=([0, loss_max]))
plt.show()
plot_history(history_2, loss_max=1)
import seaborn as sns
from sklearn import metrics
pred = xception_model.predict(eval_datagen, verbose=1)
# get most likely class
y_pred = pred.argmax(axis=1)
y_true = ytest.argmax(axis=1)
print(metrics.classification_report(y_true, y_pred))
# confusion matrix
mat = metrics.confusion_matrix(y_true, y_pred)
df_mat = pd.DataFrame(mat, index=labels, columns=labels)
plt.figure(figsize=(8,6))
sns.heatmap(df_mat, annot=True, fmt='d', cmap=plt.cm.Reds)
#plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
17/17 [==============================] - 3s 120ms/step
precision recall f1-score support
0 0.89 0.82 0.85 99
1 0.86 0.90 0.88 128
2 0.79 0.84 0.81 99
3 0.88 0.80 0.83 88
4 0.82 0.84 0.83 126
accuracy 0.84 540
macro avg 0.85 0.84 0.84 540
weighted avg 0.84 0.84 0.84 540
Text(0.5, 36.72222222222221, 'Predicted label')
N = 20 # flowers to display
fig, axes = plt.subplots(4, 5, figsize=(20,12))
for i, ax in enumerate(axes.flat):
ax.imshow(Xtest[i].astype(np.uint8))
ax.set(xticks=[], yticks=[])
true = y_true[i]
prediction = y_pred[i]
ax.set_xlabel(f'Predict: {labels[prediction]}\n True: {labels[true]}',
color='black' if true == prediction else 'red')
#fig.tight_layout()
fig.suptitle('Predicted flowers; Incorrect Labels in Red', size=14)
Text(0.5, 0.98, 'Predicted flowers; Incorrect Labels in Red')
In conclusion, we trained VGG16 and Xception models on a flower classification dataset and achieved high accuracy levels on both models. The VGG16 model achieved a test accuracy of 85%, while the Xception model achieved a slightly lower test accuracy of 84.26%. However, the Xception model had a higher validation accuracy of 87.50% compared to VGG16's 84.98%. Both models had similar training accuracies, with VGG16 at 85.18% and Xception at 84.90%. Based on these results, it can be concluded that both models performed well on the flower classification task, but the Xception model may have a slight advantage in terms of generalization performance. Further experimentation and evaluation can be done to explore the strengths and weaknesses of each model in more detail.